"""分类侧边栏组件。"""

from __future__ import annotations

from collections import defaultdict
from typing import Dict, List, Optional

from PySide6.QtCore import Signal
from PySide6.QtWidgets import (
    QComboBox,
    QFormLayout,
    QGroupBox,
    QHBoxLayout,
    QLabel,
    QLineEdit,
    QPushButton,
    QTreeWidget,
    QTreeWidgetItem,
    QVBoxLayout,
    QWidget,
)

from src.config import get_iot_model_name
from src.core.models import TypeNode


class CategoryTree(QTreeWidget):
    """用于展示分类树的侧边栏。"""

    category_selected = Signal(int)

    def __init__(self, parent=None) -> None:
        super().__init__(parent)
        self.setHeaderHidden(True)
        self.setIndentation(int(self.indentation() * 1.2))
        self._item_to_node: dict[QTreeWidgetItem, TypeNode] = {}
        self.itemClicked.connect(self._on_item_clicked)
        self._apply_styles()

    def load_nodes(self, root_nodes: list[TypeNode]) -> None:
        self.clear()
        self._item_to_node.clear()

        for node in root_nodes:
            item = self._create_item(node)
            self.addTopLevelItem(item)

        self.expandAll()

    def _create_item(self, node: TypeNode) -> QTreeWidgetItem:
        item = QTreeWidgetItem([node.name])
        self._item_to_node[item] = node

        for child in node.children:
            item.addChild(self._create_item(child))

        return item

    def _on_item_clicked(self, item: QTreeWidgetItem, column: int) -> None:  # noqa: ARG002
        node = self._item_to_node.get(item)
        if node:
            self.category_selected.emit(node.id)

    def _apply_styles(self) -> None:
        """统一分类树选中与悬停时的配色，提升对比度。"""
        self.setStyleSheet(
            """
            QTreeWidget::item:selected {
                background-color: #dbe8ff;
                color: #0b2f5b;
            }
            QTreeWidget::item:selected:active {
                background-color: #c8dcff;
                color: #0b2f5b;
            }
            QTreeWidget::item:hover {
                background-color: #eef4ff;
            }
            """
        )


class CategorySidebar(QWidget):
    """组合分类树与筛选控件的容器，提供基于物模型级类别的下拉筛选能力。"""

    category_selected = Signal(int)

    _ALL_KEY = "__all__"
    _ROOT_KEY = "__root__"

    def __init__(self, parent=None) -> None:
        super().__init__(parent)
        self._tree = CategoryTree(self)
        self._filter_label = QLabel("按物模型类别筛选")
        self._filter_selector = QComboBox(self)
        self._filter_selector.setPlaceholderText("选择物模型类别...")
        self._filter_selector.setMinimumHeight(28)

        # 缓存分类数据，避免每次切换都重新计算。
        self._root_nodes: List[TypeNode] = []
        self._node_map: Dict[int, TypeNode] = {}
        self._groups: Dict[Optional[int], List[TypeNode]] = defaultdict(list)

        # MQTT配置区域（将在setup_mqtt_config中设置）
        self._mqtt_config_box: QGroupBox | None = None
        # 数据库配置区域（将在setup_db_config中设置）
        self._db_config_box: QGroupBox | None = None

        self._setup_layout()

        self._tree.category_selected.connect(self.category_selected)
        self._filter_selector.currentIndexChanged.connect(self._on_group_changed)
    
    def setup_mqtt_config(
        self,
        host_input: QLineEdit,
        port_input: QLineEdit,
        username_input: QLineEdit,
        password_input: QLineEdit,
        username_visibility_btn: QPushButton,
        password_visibility_btn: QPushButton,
        connect_btn: QPushButton,
        status_indicator: QLabel,
        status_label: QLabel,
    ) -> None:
        """设置MQTT配置区域，将MQTT相关的widgets添加到侧边栏。
        
        Args:
            host_input: Host输入框
            port_input: Port输入框
            username_input: 用户名输入框
            password_input: 密码输入框
            username_visibility_btn: 用户名显示/隐藏按钮
            password_visibility_btn: 密码显示/隐藏按钮
            connect_btn: 连接按钮
            status_indicator: 状态指示器
            status_label: 状态标签
        """
        # 创建MQTT配置组框
        self._mqtt_config_box = QGroupBox("MQTT 连接配置")
        self._mqtt_config_box.setStyleSheet("""
            QGroupBox {
                font-weight: bold;
                font-size: 13px;
                color: #333;
                border: 2px solid #e0e0e0;
                border-radius: 6px;
                margin-top: 12px;
                padding-top: 12px;
                background-color: white;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 12px;
                padding: 0 6px;
            }
        """)
        
        form_layout = QFormLayout(self._mqtt_config_box)
        form_layout.setHorizontalSpacing(10)
        form_layout.setVerticalSpacing(8)
        form_layout.setContentsMargins(12, 16, 12, 12)
        
        # 设置输入框样式
        for field in (host_input, port_input, username_input, password_input):
            field.setMinimumHeight(28)
        
        host_input.setPlaceholderText("Broker Host")
        port_input.setPlaceholderText("Port")
        port_input.setMaximumWidth(120)
        username_input.setPlaceholderText("用户名（可选）")
        password_input.setPlaceholderText("密码（可选）")
        
        # 为用户名输入框创建容器，包含输入框和显示按钮
        username_container = QWidget()
        username_layout = QHBoxLayout(username_container)
        username_layout.setContentsMargins(0, 0, 0, 0)
        username_layout.setSpacing(8)
        username_layout.addWidget(username_input)
        username_layout.addWidget(username_visibility_btn)
        
        # 为密码输入框创建容器，包含输入框和显示按钮
        password_container = QWidget()
        password_layout = QHBoxLayout(password_container)
        password_layout.setContentsMargins(0, 0, 0, 0)
        password_layout.setSpacing(8)
        password_layout.addWidget(password_input)
        password_layout.addWidget(password_visibility_btn)
        
        # 状态容器
        status_container = QWidget()
        status_layout = QHBoxLayout(status_container)
        status_layout.setContentsMargins(0, 0, 0, 0)
        status_layout.setSpacing(8)
        status_layout.addWidget(status_indicator)
        status_layout.addWidget(status_label)
        
        # 连接按钮容器
        button_container = QWidget()
        button_layout = QHBoxLayout(button_container)
        button_layout.setContentsMargins(0, 0, 0, 0)
        button_layout.setSpacing(8)
        connect_btn.setMinimumHeight(30)
        button_layout.addWidget(connect_btn)
        button_layout.addStretch()
        
        # 添加表单行，每个参数一行
        form_layout.addRow("Host", host_input)
        form_layout.addRow("Port", port_input)
        form_layout.addRow("用户名", username_container)
        form_layout.addRow("密码", password_container)
        form_layout.addRow("状态", status_container)
        form_layout.addRow("", button_container)  # 连接按钮单独一行
        
        # 将MQTT配置区域添加到布局的顶部（在筛选和分类树之前）
        layout = self.layout()
        if layout:
            layout.insertWidget(0, self._mqtt_config_box, 0)  # 插入到顶部，不拉伸，固定大小
    
    def setup_db_config(
        self,
        host_input: QLineEdit,
        port_input: QLineEdit,
        database_input: QLineEdit,
        user_input: QLineEdit,
        password_input: QLineEdit,
        password_visibility_btn: QPushButton,
        connect_btn: QPushButton,
        status_indicator: QLabel,
        status_label: QLabel,
    ) -> None:
        """设置数据库配置区域，将数据库相关的widgets添加到侧边栏。
        
        Args:
            host_input: Host输入框
            port_input: Port输入框
            database_input: 数据库名称输入框
            user_input: 用户名输入框
            password_input: 密码输入框
            password_visibility_btn: 密码显示/隐藏按钮
            connect_btn: 连接按钮
            status_indicator: 状态指示器
            status_label: 状态标签
        """
        # 创建数据库配置组框
        self._db_config_box = QGroupBox("数据库连接配置")
        self._db_config_box.setStyleSheet("""
            QGroupBox {
                font-weight: bold;
                font-size: 13px;
                color: #333;
                border: 2px solid #e0e0e0;
                border-radius: 6px;
                margin-top: 12px;
                padding-top: 12px;
                background-color: white;
            }
            QGroupBox::title {
                subcontrol-origin: margin;
                left: 12px;
                padding: 0 6px;
            }
        """)
        
        form_layout = QFormLayout(self._db_config_box)
        form_layout.setHorizontalSpacing(10)
        form_layout.setVerticalSpacing(8)
        form_layout.setContentsMargins(12, 16, 12, 12)
        
        # 设置输入框样式
        for field in (host_input, port_input, database_input, user_input, password_input):
            field.setMinimumHeight(28)
        
        host_input.setPlaceholderText("数据库 Host")
        port_input.setPlaceholderText("Port")
        port_input.setMaximumWidth(120)
        database_input.setPlaceholderText("数据库名称")
        user_input.setPlaceholderText("用户名")
        password_input.setPlaceholderText("密码")
        
        # 为密码输入框创建容器，包含输入框和显示按钮
        password_container = QWidget()
        password_layout = QHBoxLayout(password_container)
        password_layout.setContentsMargins(0, 0, 0, 0)
        password_layout.setSpacing(8)
        password_layout.addWidget(password_input)
        password_layout.addWidget(password_visibility_btn)
        
        # 状态容器
        status_container = QWidget()
        status_layout = QHBoxLayout(status_container)
        status_layout.setContentsMargins(0, 0, 0, 0)
        status_layout.setSpacing(8)
        status_layout.addWidget(status_indicator)
        status_layout.addWidget(status_label)
        
        # 连接按钮容器
        button_container = QWidget()
        button_layout = QHBoxLayout(button_container)
        button_layout.setContentsMargins(0, 0, 0, 0)
        button_layout.setSpacing(8)
        connect_btn.setMinimumHeight(30)
        button_layout.addWidget(connect_btn)
        button_layout.addStretch()
        
        # 添加表单行，每个参数一行
        form_layout.addRow("Host", host_input)
        form_layout.addRow("Port", port_input)
        form_layout.addRow("数据库", database_input)
        form_layout.addRow("用户名", user_input)
        form_layout.addRow("密码", password_container)
        form_layout.addRow("状态", status_container)
        form_layout.addRow("", button_container)  # 连接按钮单独一行
        
        # 将数据库配置区域添加到布局中（在MQTT配置之后，筛选和分类树之前）
        layout = self.layout()
        if layout:
            # 获取当前MQTT配置的位置索引
            mqtt_index = layout.indexOf(self._mqtt_config_box) if self._mqtt_config_box else -1
            if mqtt_index >= 0:
                # 在MQTT配置之后插入数据库配置
                layout.insertWidget(mqtt_index + 1, self._db_config_box, 0)
            else:
                # 如果没有MQTT配置，添加到顶部
                layout.insertWidget(0, self._db_config_box, 0)

    def load_categories(self, root_nodes: List[TypeNode], all_nodes: List[TypeNode]) -> None:
        """加载并缓存分类数据，初始化树与下拉选择项。"""
        self._root_nodes = self._sort_nodes(root_nodes)
        self._node_map = {node.id: node for node in all_nodes}
        self._groups = self._build_groups(all_nodes)

        self._populate_filter_selector()
        self._apply_group(self._ALL_KEY)

    # ---------------- 内部工具方法 ---------------- #

    def _setup_layout(self) -> None:
        layout = QVBoxLayout(self)
        layout.setContentsMargins(8, 8, 8, 8)
        layout.setSpacing(8)

        # MQTT配置区域将在setup_mqtt_config中添加
        # 数据库配置区域将在setup_db_config中添加（在MQTT和筛选之间）
        # 这里先添加筛选和分类树
        
        filter_box = QWidget(self)
        filter_layout = QVBoxLayout(filter_box)
        filter_layout.setContentsMargins(0, 0, 0, 0)
        filter_layout.setSpacing(6)
        filter_layout.addWidget(self._filter_label)
        filter_layout.addWidget(self._filter_selector)

        layout.addWidget(filter_box, 0)
        layout.addWidget(self._tree, 1)

    def _build_groups(self, nodes: List[TypeNode]) -> Dict[Optional[int], List[TypeNode]]:
        grouped: Dict[Optional[int], List[TypeNode]] = defaultdict(list)
        for node in nodes:
            grouped[node.parent_id].append(node)

        for parent_id, items in grouped.items():
            grouped[parent_id] = self._sort_nodes(items)
        return grouped

    def _populate_filter_selector(self) -> None:
        self._filter_selector.blockSignals(True)
        self._filter_selector.clear()

        self._filter_selector.addItem("全部分类", self._ALL_KEY)

        if None in self._groups:
            root_count = len(self._groups[None])
            self._filter_selector.addItem(f"顶级分类（{root_count}）", self._ROOT_KEY)

        parent_ids = [pid for pid in self._groups.keys() if pid is not None]
        parent_ids.sort(key=self._parent_sort_key)

        for parent_id in parent_ids:
            parent_node = self._node_map.get(parent_id)
            # 使用映射表获取物模型名称，如果映射表中不存在则使用原始名称
            if parent_node:
                label = get_iot_model_name(parent_id, parent_node.name)
            else:
                label = get_iot_model_name(parent_id)
            label = f"{label}（{len(self._groups[parent_id])}）"
            self._filter_selector.addItem(label, parent_id)

        self._filter_selector.setCurrentIndex(0)
        self._filter_selector.blockSignals(False)

    def _on_group_changed(self, index: int) -> None:
        if index < 0:
            return
        key = self._filter_selector.itemData(index)
        self._apply_group(key)

    def _apply_group(self, key) -> None:
        if key == self._ALL_KEY:
            self._tree.load_nodes(self._root_nodes)
            return

        if key == self._ROOT_KEY:
            nodes = self._groups.get(None, [])
            self._tree.load_nodes(nodes)
            return

        parent_node = self._node_map.get(key) if isinstance(key, int) else None
        if parent_node:
            # 当用户选择具体物模型级节点时，将物模型级节点自身作为根传入，保留上下文层级。
            self._tree.load_nodes([parent_node])
            return

        nodes = self._groups.get(key, [])
        self._tree.load_nodes(nodes)

    def _parent_sort_key(self, parent_id: Optional[int]) -> tuple[int, str]:
        if parent_id is None:
            return (0, "")
        parent_node = self._node_map.get(parent_id)
        if not parent_node:
            return (0, str(parent_id))
        return (parent_node.sort_key(), parent_node.name)

    @staticmethod
    def _sort_nodes(nodes: List[TypeNode]) -> List[TypeNode]:
        return sorted(nodes, key=lambda n: (n.sort_key(), n.name))


